package cn.maihe.elg.operation.utils;


import cn.maihe.elg.operation.model.dto.InnerBaseResp;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import com.alibaba.fastjson.JSONObject;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

import javax.net.ssl.X509TrustManager;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * OKHttp请求工具类
 */
@Slf4j
public class OkHttpUtils {


    private static final int SUCCESS_CODE = 200;

    private static final int CONNECT_TIMEOUT = 20;
    private static final int READ_TIMEOUT = 60;
    private static final int WRITE_TIMEOUT = 60;
    private static final X509TrustManager MANAGER = SSLSocketClientUtil.getX509TrustManager();
    private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder().protocols(Collections.singletonList(Protocol.HTTP_1_1)).connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS).readTimeout(READ_TIMEOUT, TimeUnit.SECONDS).writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS).sslSocketFactory(SSLSocketClientUtil.getSocketFactory(MANAGER), MANAGER).hostnameVerifier(SSLSocketClientUtil.getHostnameVerifier()).build();

    private static String HTTP_JSON = "application/json; charset=utf-8";
    private static String HTTP_FORM = "application/x-www-form-urlencoded; charset=utf-8";
    private static String HTTP_MULTIPART = "multipart/form-data";
    private static String HTTP = "http";
    private static String HTTPS = "https";

    /**
     * Get请求
     * 响应主体大于1 MB,建议使用流处理，不要直接以字符操作
     *
     * @param url 请求地址
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpGet(String url) {
        NR<String> checkResult = checkUrl(url);
        if (checkResult.isFail()) {
            return checkResult;
        }
        return httpGet(url, Collections.emptyMap());
    }


    /**
     * Get请求携带请求头
     * 响应主体大于1 MB,建议使用流处理
     *
     * @param url     请求地址
     * @param headers 请求头
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpGet(String url, Map<String, String> headers) {
        return httpGet(url, headers, Collections.emptyMap());
    }

    /**
     * Get请求携带请求头
     * 响应主体大于1 MB,建议使用流处理
     *
     * @param url     请求地址
     * @param headers 请求头
     * @param map     请求参数
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpGet(String url, Map<String, String> headers, Map<String, Object> map) {
        NR<String> checkResult = checkUrl(url);
        if (checkResult.isFail()) {
            return checkResult;
        }

        HttpUrl.Builder httpBuilder = HttpUrl.parse(url).newBuilder();
        if (!map.isEmpty()) {
            map.forEach((key, value) -> httpBuilder.addQueryParameter(key, String.valueOf(value)));
        }
        Request.Builder builder = new Request.Builder();
        if (!headers.isEmpty()) {
            headers.forEach(builder::header);
        }
        Request request = builder.get().url(httpBuilder.build()).build();
        return prepareResult(request, map.toString());
    }

    /**
     * Post请求，无携带请求头，参数为JSON格式
     *
     * @param url 请求地址
     * @param map 请求参数，map格式
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpPost(String url, Map<String, Object> map) {
        return httpPost(url, Collections.emptyMap(), map);
    }

    /**
     * Post请求，携带请求头，参数为JSON格式
     *
     * @param url     请求地址
     * @param map     请求参数，map格式
     * @param headers 请求头
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpPost(String url, Map<String, String> headers, Map<String, Object> map) {
        NR<String> checkResult = checkUrl(url);
        if (checkResult.isFail()) {
            return checkResult;
        }
        //Post请求参数
        FormBody.Builder builder = new FormBody.Builder();
        map.forEach((key, value) -> builder.add(key, String.valueOf(value)));
        Request.Builder requestBuild = new Request.Builder().url(url);
        if (!headers.isEmpty()) {
            headers.forEach(requestBuild::addHeader);
        }

        Request request = requestBuild.post(builder.build()).build();

        return prepareResult(request, map.toString());
    }

    /**
     * Post请求，无携带请求头，参数为JSON格式
     *
     * @param url  请求地址
     * @param json 请求参数，JSON格式
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpPost(String url, String json) {
        return httpPost(url, Collections.emptyMap(), json);
    }

    /**
     * Post请求，无携带请求头，参数为Object，自动转换为Json格式
     *
     * @param url         请求地址
     * @param objectParam 请求参数,会转换成Json
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpPost(String url, Object objectParam) {
        return httpPost(url, Collections.emptyMap(), JSONObject.toJSONString(objectParam));
    }

    /**
     * Post请求，携带请求头，参数为JSON格式
     *
     * @param url     请求地址
     * @param json    请求参数，JSON格式
     * @param headers 请求头
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpPost(String url, Map<String, String> headers, String json) {
        NR<String> checkResult = checkUrl(url);
        if (checkResult.isFail()) {
            return checkResult;
        }

        MediaType mediaType = MediaType.parse(HTTP_JSON);
        RequestBody body = RequestBody.create(mediaType, json);
        Request.Builder requestBuilder = new Request.Builder().url(url);
        if (!headers.isEmpty()) {
            headers.forEach(requestBuilder::addHeader);
        }
        Request request = requestBuilder.post(body).build();
        return prepareResult(request, json);
    }


    /**
     * Post请求，无携带请求头，参数为Object，自动转换为Json格式
     *
     * @param url    请求地址
     * @param params 请求参数,会转换成Json
     * @return 响应结果
     */
    @NonNull
    public static NR<String> postDataByForm(String url, Map<String, Object> params) {
        return postDataByForm(url, Collections.emptyMap(), params);
    }

    /**
     * Post请求，无携带请求头，参数为Object，自动转换为Json格式
     *
     * @param url    请求地址
     * @param params 请求参数,会转换成Json
     * @return 响应结果
     */
    @NonNull
    public static InnerBaseResp postDataByFormV2(String url, Map<String, Object> params, String apiString) {
        NR<String> stringNR = postDataByForm(url, Collections.emptyMap(), params);
        return buildInnerBaseResp(stringNR,apiString);
    }

    private static InnerBaseResp buildInnerBaseResp(NR<String> stringNR, String apiString) {
        if (stringNR.isFail()) {
            // 需要测试时候才能确定返回结果是否为JSON格式
            log.error("通知异常:{}", stringNR.getErrorMessage());
            return InnerBaseResp.error("通知异常:" + stringNR.getErrorMessage());
        }

        JSONObject jsonObject = JSONObject.parseObject(stringNR.getData());
        if (!Boolean.TRUE.equals(jsonObject.get("success"))) {
            log.error("通知异常:{} ErrorCode:{}", jsonObject.get("errorDesc"), jsonObject.get("errorCode"));
            return InnerBaseResp.error("通知异常:" + jsonObject.get("errorDesc") + " ErrorCode: " + jsonObject.get("errorCode"));
        }

        log.info("通知成功:{}, 接口：{}", CenterNoEnum.XIAMEN, apiString);
        return InnerBaseResp.success(null, "");
    }


    /**
     * 参数以表单格式提交，并携带请求头
     * 表单格式为：application/x-www-form-urlencoded; charset=utf-8，
     * 参数格式为：param1=1&param2=2
     *
     * @param url     请求地址
     * @param content 请求内容，application/x-www-form-urlencoded格式
     * @param headers 请求头
     * @return 响应结果
     */
    @NonNull
    public static NR<String> postDataByForm(String url, Map<String, String> headers, Map<String, Object> content) {
        NR<String> checkResult = checkUrl(url);
        if (checkResult.isFail()) {
            return checkResult;
        }

        String postData = formatParamWithForm(content);

        MediaType mediaType = MediaType.parse(HTTP_FORM);
        RequestBody body = RequestBody.create(mediaType, postData);
        Request.Builder requestBuilder = new Request.Builder().url(url);
        if (!headers.isEmpty()) {
            headers.forEach(requestBuilder::addHeader);
        }
        Request request = requestBuilder.post(body).build();
        return prepareResult(request, postData);
    }


    /**
     * Post请求，携带请求头，参数为 multipart/form-data 格式
     *
     * @param url    请求地址
     * @param params 文本参数
     * @param files  文件参数
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpPostMultipart(String url, Map<String, Object> params, Map<String, File> files) {
        return httpPostMultipart(url, Collections.emptyMap(), params, files);
    }

    /**
     * Post请求，携带请求头，参数为 multipart/form-data 格式
     *
     * @param url     请求地址
     * @param headers 请求头
     * @param params  文本参数
     * @param files   文件参数
     * @return 响应结果
     */
    @NonNull
    public static NR<String> httpPostMultipart(String url, Map<String, String> headers, Map<String, Object> params, Map<String, File> files) {
        NR<String> checkResult = checkUrl(url);
        if (checkResult.isFail()) {
            return checkResult;
        }

        RequestBody requestBody = createMultipartBody(params, files);
        Request.Builder requestBuilder = new Request.Builder().url(url);

        if (!headers.isEmpty()) {
            headers.forEach(requestBuilder::addHeader);
        }

        Request request = requestBuilder.post(requestBody).build();
        return prepareResult(request, "multipart/form-data"); // 这里传递一个标识，表示是multipart请求
    }

    /**
     * 创建 multipart/form-data 请求体
     *
     * @param params 文本参数
     * @param files  文件参数
     * @return 请求体
     */
    private static RequestBody createMultipartBody(Map<String, Object> params, Map<String, File> files) {
        MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        builder.setType(MediaType.parse(HTTP_MULTIPART));

        // 添加文本参数
        if (params != null && !params.isEmpty()) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                builder.addFormDataPart(entry.getKey(), String.valueOf(entry.getValue()));
            }
        }

        // 添加文件参数
        if (files != null && !files.isEmpty()) {
            for (Map.Entry<String, File> entry : files.entrySet()) {
                File file = entry.getValue();
                builder.addFormDataPart(entry.getKey(), file.getName(), RequestBody.create(MediaType.parse("application/octet-stream"), file));
            }
        }

        return builder.build();
    }

    /**
     * 生成'a=1&b=2'格式的请求参数
     *
     * @param params Request Params
     * @return formatted Params
     */
    public static String formatParamWithForm(Map<String, Object> params) {
        // 构建表单参数的字符串
        StringBuilder postData = new StringBuilder();
        if (params != null && !params.isEmpty()) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                if (entry.getValue() != null) {
                    // URL encode the key and value to handle special characters
                    try {
                        postData.append(URLEncoder.encode(entry.getKey(), "UTF-8")).append("=").append(URLEncoder.encode(entry.getValue().toString(), "UTF-8")).append("&");
                    } catch (UnsupportedEncodingException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            // 删除最后一个"&"符号
            postData.deleteCharAt(postData.length() - 1);
        }
        return postData.toString();
    }

    /**
     * 处理请求结果
     *
     * @param request OKHttp请求对象
     * @return 处理结果
     */
    @NonNull
    private static NR<String> prepareResult(Request request) {
        return prepareResult(request, "");
    }

    /**
     * 处理请求结果
     *
     * @param request OKHttp请求对象
     * @param param   请求参数
     * @return 处理结果
     */
    @NonNull
    private static NR<String> prepareResult(Request request, String param) {
        try {
            Response response = OK_HTTP_CLIENT.newCall(request).execute();
            if (response.code() == SUCCESS_CODE) {
                log.info("{} 请求成功; [url={}, param={}]", request.method(), request.url().url(), param);
                if (response.body() != null) {
                    final ResponseBody body = response.body();
                    InputStream inputStream = body.byteStream();
                    final byte[] bytes = inputToByte(inputStream);
                    return NR.success(new String(bytes), response.message(), String.valueOf(response.code())).setByteData(bytes);
                }
                return NR.success(null, "请求成功，返回结果为空", String.valueOf(response.code()));
            } else {
                log.warn("{} 请求失败; [ErrorCode = {} , url={}, param={}]", request.method(), response.code(), request.url().url(), param);
                if (response.body() != null) {
                    return NR.fail(String.valueOf(response.code()), "请求失败", response.body().string());
                }
                return NR.fail(String.valueOf(response.code()), "请求成功，返回结果为空", response.message());
            }
        } catch (IOException e) {
            return NR.error(e, String.format("Http Post Form请求失败,url: %s", request.url().url()));
        }
    }

    /**
     * 流转byte数组
     *
     * @param inputStream
     * @return
     * @throws IOException
     */
    private static byte[] inputToByte(InputStream inputStream) throws IOException {
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        byte[] buff = new byte[1024];
        int rc;
        while ((rc = inputStream.read(buff, 0, 1024)) > 0) {
            swapStream.write(buff, 0, rc);
        }
        return swapStream.toByteArray();
    }

    /**
     * 验证请求地址是否合法
     *
     * @param url 请求地址
     * @return 验证结果
     */
    private static NR<String> checkUrl(String url) {
        if (url == null || url.isEmpty()) {
            log.error("请求地址为空或!");
            return NR.fail("请求地址为空!");
        }
        if (!url.startsWith(HTTP) && !url.startsWith(HTTPS)) {
            log.error("URL的Scheme必须为Http或Https");
            return NR.fail("请求地址为的Scheme必须包含Http或Https!");
        }
        return NR.success("");
    }

    public static void main(String[] args) {
//        String url = "https://www.baidu.com";
//        NR<String> stringNR = httpGet(url);
//        System.out.println(stringNR.getCode() + "::" + stringNR.isSuccess() + "::" + stringNR.isFail() + "::" + stringNR.getReturnCode() + "::" + stringNR.getMsg());
//        System.out.println(stringNR);

        JSONObject obj = new JSONObject();
        obj.put("accountId", "CAnwFbOUCbwICfAVs5QJvC7J6gpVrdRPJWu1kTFXf5wnb5ceSl9OE5UK08x6fxT6Ntrt883m6AA");
        String url = "https://etest2.spdb.com.cn/spdb/uat/api/partner/account/queryAccount";

        Map<String, String> header = new HashMap<>();
        header.put("X-SPDB-Client-Id", "d83fa07c-0b7f-40e5-b53a-8c21f3c0437f");
        header.put("X-SPDB-SIGNATURE", "nS6uP3oO5wK4dY8uX4aN0tT7hH6rU8jU7oW6aE7aI6iV6qO4qD");


        NR<String> stringNR = httpPost(url, header, obj.toString());
        //String jsonSTR="{\"accountId\": \"CAnwFbOUCbwICfAVs5QJvGsibK6O0tRQq4fAKmf8SiblQnb5ceSl9OE1mibtn09U3FONtrt883m6AA\"}";
        System.out.println(obj.toString());
        System.out.println(stringNR.getErrorMessage());
    }
}
